package com.alogic.loki;

import com.alogic.uid.IdGenerator;
import com.alogic.uid.impl.Snowflake;
import com.alogic.xscript.AbstractLogiclet;
import com.alogic.xscript.ExecuteWatcher;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.doc.XsObject;
import com.alogic.xscript.plugins.Segment;
import com.anysoft.selector.Selector;
import com.anysoft.stream.Handler;
import com.anysoft.util.*;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.ArrayList;
import java.util.List;

/**
 * 相关的插件
 *
 * @since 1.6.13.14 [20200907 duanyy] <br>
 */
public class Plugins extends Segment {
    public Plugins(String tag, Logiclet p) {
        super(tag, p);

        registerModule("loki-new",New.class);
        registerModule("loki-set-label",SetLabel.class);
        registerModule("loki-set-labels",SetLabels.class);
        registerModule("loki-line",AddLine.class);
        registerModule("loki-as-json",AsJson.class);
        registerModule("loki-commit",Commit.class);
    }

    public static class New extends Plugins {
        /**
         * id
         */
        protected String $id = "";

        protected String $domain = "";

        protected String $type = "";

        protected String $level = "";

        /**
         * 上下文id
         */
        protected String cid = "$loki-stream";

        /**
         * 用来生成id
         */
        protected IdGenerator uid = null;

        public New(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);
            $id = PropertiesConstants.getRaw(p,"id", "");
            $domain = PropertiesConstants.getRaw(p,"domain",$domain);
            $level = PropertiesConstants.getRaw(p,"level",$level);
            $type = PropertiesConstants.getRaw(p,"type",$type);
            cid = PropertiesConstants.getString(p,"cid",cid,true);
            uid = new Snowflake();
            uid.configure(p);
        }

        @Override
        protected void onExecute(XsObject root, XsObject current, LogicletContext ctx,
                                 ExecuteWatcher watcher) {
            String id = PropertiesConstants.transform(ctx,$id,"");
            if (StringUtils.isEmpty(id)){
                id = uid.nextId();
            }
            try{
                LokiStream data = new LokiStream(id);
                data.setLabel("domain",PropertiesConstants.transform(ctx,$domain,""),true);
                data.setLabel("type",PropertiesConstants.transform(ctx,$type,""),true);
                data.setLabel("level",PropertiesConstants.transform(ctx,$level,""),true);
                ctx.setObject(cid,data);
                super.onExecute(root,current,ctx,watcher);
            }finally {
                ctx.removeObject(cid);
            }
        }
    }

    /**
     * LokiStream构造器
     */
    public abstract static class Builder extends AbstractLogiclet{
        /**
         * 上下文id
         */
        protected String pid = "$loki-stream";

        public Builder(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);
            pid = PropertiesConstants.getString(p,"pid",pid,true);
        }
        @Override
        protected void onExecute(XsObject root,XsObject current, LogicletContext ctx,
                                 ExecuteWatcher watcher) {
            LokiStream data = ctx.getObject(pid);
            if (data != null){
                onExecute(data,root,current,ctx,watcher);
            }else{
                log(String.format("[%s]Can not find loki stream object:%s",this.getXmlTag(),pid));
            }
        }

        protected abstract void onExecute(LokiStream data, XsObject root,XsObject current, LogicletContext ctx,ExecuteWatcher watcher);
    }

    /**
     * 设置多个label
     */
    public static class SetLabels extends Builder {
        /**
         * 选择器列表
         */
        protected List<Selector> properties = new ArrayList<Selector>();

        public SetLabels(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Element e, Properties p) {
            Properties props = new XmlElementProperties(e,p);
            configure(props);

            NodeList nodeList = XmlTools.getNodeListByPath(e, "property");
            for (int i = 0 ;i < nodeList.getLength() ; i ++){
                Node n = nodeList.item(i);
                if (n.getNodeType() != Node.ELEMENT_NODE){
                    continue;
                }
                Element elem = (Element)n;
                try{
                    Selector selector = Selector.newInstance(elem, p);
                    if (selector != null){
                        properties.add(selector);
                    }
                }catch (Exception ex){
                    log(String.format("Can not create selector with %s",XmlTools.node2String(elem)),LOG_ERROR);
                }
            }
        }

        @Override
        protected void onExecute(LokiStream data, XsObject root, XsObject current,
                                 LogicletContext ctx, ExecuteWatcher watcher) {
            for (Selector p:properties){
                String k = p.getId();
                String v = p.select(ctx);
                data.setLabel(k,v,p.isFinal());
            }
        }
    }

    /**
     * 设置单个label
     */
    public static class SetLabel extends Builder {
        protected String $key = "";
        protected String $value = "";
        protected String $dft = "";
        protected boolean overwrite = true;

        public SetLabel(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);

            $key = PropertiesConstants.getRaw(p,"key",$key);
            $value = PropertiesConstants.getRaw(p,"value",$value);
            $dft = PropertiesConstants.getRaw(p,"dft",$dft);
            overwrite = PropertiesConstants.getBoolean(p, "overwrite", true);
        }

        @Override
        protected void onExecute(LokiStream data, XsObject root, XsObject current,
                                 LogicletContext ctx, ExecuteWatcher watcher) {
            String k = PropertiesConstants.transform(ctx, $key, "");
            if (StringUtils.isNotEmpty(k)){
                String v = PropertiesConstants.transform(ctx, $value, "");
                if (StringUtils.isEmpty(v)){
                    v = PropertiesConstants.transform(ctx, $dft, "");
                }
                if (StringUtils.isNotEmpty(v)){
                    data.setLabel(k,v,overwrite);
                }
            }
        }
    }

    /**
     * 获取id值
     */
    public static class GetId extends Builder {
        protected String $id = "";
        public GetId(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);
            $id = PropertiesConstants.getRaw(p,"id",$id);
        }

        @Override
        protected void onExecute(LokiStream data, XsObject root, XsObject current,
                                 LogicletContext ctx, ExecuteWatcher watcher) {
            String id = PropertiesConstants.transform(ctx,$id,"");
            if (StringUtils.isNotEmpty(id)){
                ctx.SetValue(id,data.id());
            }
        }
    }

    /**
     * 获取label值
     */
    public static class GetLabel extends Builder {
        protected String $id = "";
        protected String $key = "";
        protected String $dft = "";
        protected boolean overwrite = true;

        public GetLabel(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);

            $id = PropertiesConstants.getRaw(p,"id",$id);
            $key = PropertiesConstants.getRaw(p,"key",$key);
            $dft = PropertiesConstants.getRaw(p,"dft",$dft);
            overwrite = PropertiesConstants.getBoolean(p, "overwrite", true);
        }

        @Override
        protected void onExecute(LokiStream data, XsObject root, XsObject current,
                                 LogicletContext ctx, ExecuteWatcher watcher) {
            String id = PropertiesConstants.transform(ctx,$id,"");
            if (StringUtils.isNotEmpty(id)){
                String value = null;
                String key = PropertiesConstants.transform(ctx,$key,"");
                if (StringUtils.isEmpty(key)){
                    value = PropertiesConstants.getString(ctx,id,"");
                }else{
                    value = PropertiesConstants.getString(ctx,$key,"");
                }
                if (StringUtils.isEmpty(value)){
                    value = PropertiesConstants.transform(ctx,$dft,"");
                }
                if (StringUtils.isNotEmpty(value)){
                    ctx.SetValue(id,value);
                }
            }
        }
    }

    /**
     * 增加序列值
     */
    public static class AddLine extends Builder {
        protected String $tt = "";
        protected String $line = "";

        public AddLine(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);

            $tt = PropertiesConstants.getRaw(p,"tt",$tt);
            $line = PropertiesConstants.getRaw(p,"line",$line);
        }

        @Override
        protected void onExecute(LokiStream data, XsObject root, XsObject current,
                                 LogicletContext ctx, ExecuteWatcher watcher) {
            long tt = PropertiesConstants.transform(ctx,$tt,0);
            if (tt <= 0){
                tt = System.currentTimeMillis() * 1000000L + System.nanoTime() % 1000000L;
            }
            String line = PropertiesConstants.transform(ctx,$line,"");
            if (StringUtils.isNotEmpty(line)){
                data.addValue(tt,line);
            }
        }
    }

    /**
     * 以Json的形式输出到变量
     */
    public static class AsJson extends Builder {
        protected String $id = "";
        public AsJson(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);
            $id = PropertiesConstants.getRaw(p,"id",$id);
        }

        @Override
        protected void onExecute(LokiStream data, XsObject root, XsObject current,
                                 LogicletContext ctx, ExecuteWatcher watcher) {
            String id = PropertiesConstants.transform(ctx,$id,"");
            if (StringUtils.isNotEmpty(id)){
                StringBuffer buffer = new StringBuffer();
                LokiStream.writeToBuffer(data,buffer);
                ctx.SetValue(id,buffer.toString());
            }
        }
    }

    /**
     * 提交到Handler流
     */
    public static class Commit extends Builder {
        public Commit(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);
        }

        @Override
        protected void onExecute(LokiStream data, XsObject root, XsObject current,
                                 LogicletContext ctx, ExecuteWatcher watcher) {
            Handler<LokiStream> handler = LokiHandlerFactory.getHandler();
            if (handler != null){
                handler.handle(data,System.currentTimeMillis());
            }
        }
    }
}
